--
-- @Author:			
-- @DateTime:	2019-03-30 23:05:48
-- @Description: 消息协议的解析

local skynet = require "skynet"
local log = require "Logger"
local cjson = require "cjson"
local MessageMap = require "MessageMap"
local protobuf --= require "protobuf" 

-- require "MessageProtocol"


--协议类型
local PROTOCOL = {
	normal = 0, --通用协议
	router = 1,	--路由协议
}

--数据包类型
local PACKET_TYPE = {
    normal = 0, --普通数据
    crc = 0x01, --crc校验
    dic = 0x02, --dic加密
    des = 0x04, --des加密
    rsa = 0x08, --rsa加密
	tar = 0x10, --数据压缩
}

--序列类型
local SERIALIZE_TYPE = {
	binary		= 0, --二进制流格式
	protobuf 	= 1, --pb格式
	json 		= 2, --json格式
	xml 		= 3, --xml格式
}



--消息打包类
local MessagePack = class("MessagePack")


function MessagePack:ctor()

	self.msg_head_ident = 'yznc' 	--消息协议标识
	self.msg_version = 1			--消息协议版本
	self.msg_head_size = 24 		--消息协议头长度
	self.msg_router_size = 28		--消息路由头长度
	self.msg_command_size = 12 		--消息命令头长度
	self.message_map = MessageMap.new()

end

--??
function MessagePack:initProto(pbc_env)
	assert(pbc_env)
	debug.getregistry().PROTOBUF_ENV = pbc_env
	protobuf = require "protobuf"
	self.message_map:init()
end

--组装pb消息
function MessagePack:encodePbMsg(main_cmd_id, sub_cmd_id, message_body)

	local message_name = self.message_map:getMessageName(main_cmd_id, sub_cmd_id)
	local message_data = ''
	-- print("__encodePbMsg_______",message_name, message_body)
	if next(message_body) then
		local status
		status, message_data = pcall(protobuf.encode, message_name, message_body)
		if not status then
			log.error('######encode message body failed', message_name,message_body)
			return
		end
	end

	return message_data
end

--解析pb消息
function MessagePack:decodePbMsg(main_cmd_id, sub_cmd_id, message_buf, message_size)

	if message_buf == nil or #message_buf == 0 then
		log.error('######decodePbMsg message body invalid package', main_cmd_id, sub_cmd_id)
		return
	end

	local message_name = self.message_map:getMessageName(main_cmd_id, sub_cmd_id)  
	local status, message_body = pcall(protobuf.decode, message_name, message_buf, message_size or #message_buf)  
	
	if not status then
		log.error('######decodePbMsg message body invalid package', main_cmd_id, sub_cmd_id, message_body)
		return 
	end

	return message_body
end


-- //网络消息协议头
-- struct NET_MSG_HEADER 
-- {
-- 	uint8	head_ident[4]; 	//头标识(4字节)
-- 	uint16	head_size;		//头部大小
-- 	uint8	version;		//协议版本
-- 	uint8	protocol;		//消息协议
-- 	uint32	packet_size;   	//数据包大小
-- 	uint32	check_cdoe;		//数据校验码
-- 	uint8	packet_type;	//数据包类型
-- 	uint8	serialize_type;	//序列类型
-- 	uint16	reserve0;		//保留字段
-- 	uint32	reserve1;		//保留字段
-- };

-- 设置网络消息协议头
function MessagePack:setHeader(head, packet_size, serialize_type)

	if not head then 
		head = {
			head_ident = self.msg_head_ident,
			head_size = self.msg_head_size,
			version = self.msg_version,
			protocol = PROTOCOL.normal,
			packet_size = packet_size or self.msg_head_size,
			check_cdoe = 0,
			packet_type = PACKET_TYPE.normal,
			serialize_type = serialize_type or SERIALIZE_TYPE.protobuf,
			reserve0 = 0,
			reserve1 = 0,
		}
	end

	local str = '' --4+2+1+1+4+4+1+1+2+4 = 24
	str = str .. string.pack(">c4", head.head_ident)
	str = str .. string.pack(">H", head.head_size)
	str = str .. string.pack(">B", head.version)
	str = str .. string.pack(">B", head.protocol)
	str = str .. string.pack(">I4", head.packet_size)
	str = str .. string.pack(">I4", head.check_cdoe)
	str = str .. string.pack(">B", head.packet_type)
	str = str .. string.pack(">B", head.serialize_type)
	str = str .. string.pack(">H", head.reserve0)
	str = str .. string.pack(">I4", head.reserve1)

	return str
end

--获取网络消息协议头
function MessagePack:getHeader(message)
	
	if string.len(message) < self.msg_head_size then 
		log.error("############ MessagePack:getHeader invalid message_size " .. string.len(message))
		return 
	end
	
	return {        
		head_ident 	= string.unpack(">c4", message:sub(1,4)),
		head_size 	= string.unpack(">H", message:sub(5,6)),
		version 	= string.unpack(">B", message:sub(7,7)),
		protocol 	= string.unpack(">B", message:sub(8,8)),
		packet_size = string.unpack(">I4", message:sub(9,12)),
		check_cdoe 	= string.unpack(">I4", message:sub(13,16)),
		packet_type = string.unpack(">B", message:sub(17,17)),
		serialize_type = string.unpack(">B", message:sub(18,18)),
		reserve0 = string.unpack(">H", message:sub(19,20)),
		reserve1 = string.unpack(">I4", message:sub(21,24)),    
	}

end


--设置网络消息命令
function MessagePack:setCommand(command)

	if not command then 
		command = {
			main_cmd_id = 0,
			sub_cmd_id = 0,
			data_size = 0,
		}
	end
	
	local str = '' --4+4+4 = 12
	str = str .. string.pack(">I4", command.main_cmd_id)
	str = str .. string.pack(">I4", command.sub_cmd_id)
	str = str .. string.pack(">I4", command.data_size)

	return str
end

--获取网络消息命令
function MessagePack:getCommand(message)

	if string.len(message) < self.msg_command_size then 
		log.error("############ MessagePack:getCommand invalid message_size " .. string.len(message))
		return 
	end

	return {
		main_cmd_id = string.unpack(">I4", message:sub(1,4)),
		sub_cmd_id  = string.unpack(">I4", message:sub(5,8)),
		data_size 	= string.unpack(">I4", message:sub(9,12)),
	}
end


--打包网络消息
function MessagePack:packClient(main_cmd_id, sub_cmd_id, message_body)
	
	local header = {
		head_ident = self.msg_head_ident,
		head_size = self.msg_head_size,
		version = self.msg_version,
		protocol = PROTOCOL.normal,
		packet_size = self.msg_head_size,
		check_cdoe = 0,
		packet_type = PACKET_TYPE.normal,
		serialize_type = SERIALIZE_TYPE.protobuf,
		reserve0 = 0,
		reserve1 = 0,
	} 

	local command = {
		main_cmd_id = main_cmd_id or 0,
		sub_cmd_id = sub_cmd_id or 0,
		data_size = 0,
	}

	local pack_data = ""
	local message_data = ""
	
	if message_body and next(message_body) then
		message_data = self:encodePbMsg(main_cmd_id, sub_cmd_id, message_body)
	end

	command.data_size = #message_data
	header.packet_size = self.msg_head_size + self.msg_command_size + #message_data
	pack_data = pack_data .. self:setHeader(header, header.packet_size)
	pack_data = pack_data .. self:setCommand(command)
	pack_data = pack_data .. message_data
	
	return pack_data
end


--打包网络消息
function MessagePack:packClientByName(message_name, message_body)
	local cmd = self.message_map:getMessageId(message_name)
	if not cmd then 
		log.error("消息协议id不对__",message_name)
	end
	return self:packClient(cmd.main_id, cmd.sub_id, message_body)	
end


--解包网络消息
function MessagePack:unpackClient(message_buf, message_size)

	if not message_size then message_size = #message_buf end
	if message_size < self.msg_head_size + self.msg_command_size then
		log.error("############ unpack invalid message_size ".. message_size)
		return 
	end
	local message
	if type(message_buf) == 'string' then 
		message = message_buf
	else
		message = skynet.tostring(message_buf, message_size)  
	end	
	local header = self:getHeader(message)
	local command = self:getCommand(message:sub(self.msg_head_size + 1))
	local main_cmd_id = command.main_cmd_id --主命令标识
	local sub_cmd_id = command.sub_cmd_id --子命令标识
	-- local callback = self.message_map:getCallback(main_cmd_id, sub_cmd_id)
	local message_name = self.message_map:getMessageName(main_cmd_id, sub_cmd_id)
	local message_body = {}	
	if command.data_size > 0 then
		message_body = self:decodePbMsg(main_cmd_id, sub_cmd_id, message:sub(self.msg_head_size + self.msg_command_size + 1))
	end
	-- print("__MessagePack:unpackClient_ message_name",message_name)
	-- print("__MessagePack:unpackClient_ header_",header)
	-- print("__MessagePack:unpackClient_ command_",command)
	-- print("__MessagePack:unpackClient_ message_body_",message_body)

	return { callback = message_name, header = header, command = command,  message_body = message_body }
end



return MessagePack
